package com.gagakuai.concurrent;

import com.gagakuai.concurrent.locks.AbstactQueuedSychronier;

import java.io.Serializable;


/*
 * @program: settement
 * @description: 信号量
 * // TODO: 2022/11/21
 * 允许多个线程同时工作，共同工作的线程数量== permission 数量。
 * 内部实现了两种锁： 【共享公平锁】和共享非公平锁
 * 初始化锁的实现 会产生一定数量的许可证，
 * 在加锁的过程中可以看成是 减少凭证
 * 在释放锁的过程中 可以看成是 增加凭证
 * 注意，并未实现锁的定义
 * @author: houhong
 * @create: 2022-11-21 00:10
 */
public class Semaphore implements Serializable {


    private static final long serialVersionUID = 3095022496146524139L;

    private final Sync sync;

    /*
     * 默认为非公平锁
     */
    public Semaphore(int permits) {
        sync = new NonFairSync(permits);
    }

    /*
     * 根据条件选择
     */
    public Semaphore(int permits, boolean fair) {

        sync = fair ? new FairSync(permits) : new NonFairSync(permits);
    }

    /*
     * 申请共享锁，不允许阻塞带有中断标记的锁
     */
    public void acquire() throws InterruptedException {

        sync.acquireSharedInterruptibly(1);
    }

    /*
     * 申请共享锁，允许阻塞带有中断标记的线程（会先将其标记清除）
     */
    public void acquireUninterruptibly() {

        sync.acquireShared(1);
    }

    /*
     * 释放锁 并且唤醒下一个节点
     */
    public void release() {
        sync.releaseShared(1);
    }


    static final class NonFairSync extends Sync {

        private static final long serialVersionUID = -1839366965667103999L;

        public NonFairSync(int permits) {
            super(permits);
        }
    }

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -984841025938755128L;

        public FairSync(int permits) {
            super(permits);
        }

        @Override
        protected int tryAcquiredShared(int arg) {
            return fairTryAcquireShared(arg);
        }
    }

    /*
     * 同步队列的实现者，实现了锁的语义
     * 许可证AQS.state == 0 当前锁的许可证全部借出去，后续线程只能等待
     * 许可证AQS.state >= 0  当前锁仍然有可用的许可证，后来的线程可以尝试申请锁
     * <p>
     * *    当线程申请锁时，需要借出许可证，许可证数量减少，直到为0
     * *    当线程释放锁时，需要归还许可证，许可证数量增加，直到设定的初始值
     */
    static abstract class Sync extends AbstactQueuedSychronier {


        private static final long serialVersionUID = 4416086052454635242L;

        public Sync(int permits) {
            setState(permits);
        }

        /*
         * @param acquires
         * @return {@link int}
         * @Author houhong
         * @Description //TODO 允许单个或者多个线程 多次申请锁
         * @Date 12:27 上午 2022/11/21
         */
        final int fairTryAcquireShared(int acquires) {

            for (; ; ) {

                if (hasQueuedPredecessors()) {
                    return -1;
                }

                //获取当前剩余的凭证
                int avalible = getState();
                //借出一定数量的许可证后 还剩下的许可证
                int remaining = avalible - acquires;

                // 如果许可证数量不足，说明本次抢锁失败
                if (remaining < 0) {
                    return remaining;
                }

                //有剩下的，CAS 更新
                if (compareAndSetState(avalible, remaining)) {
                    return remaining;
                }
            }
        }




        /*
         * 释放锁,即归还凭证 这里会并发
         */
        @Override
        protected boolean tryReleaseShared(int release) {

            for (; ; ) {

                int current = getState();
                //这里不会线程安全问题
                int next = current + release;

                if (next < current) {
                    // 归还许可证之后许可证数量应该增加而不是减少
                    throw new Error("Maximum permit count exceeded");
                }

                //原子性的归还
                if (compareAndSetState(current, next)) {
                    return true;
                }

            }


        }
    }

}